package org.example.uhy.domain.model.coupon;

import org.example.uhy.domain.model.base.Operator;
import org.example.uhy.domain.model.base.TimeRange;
import org.example.uhy.domain.model.fans.FansId;
import org.example.uhy.domain.model.member.MemberId;
import org.example.uhy.exceptions.BusinessException;
import org.example.uhy.supports.meta.Aggregate;

import java.time.LocalDateTime;
import java.util.function.Consumer;

/**
 * 卡券
 * <p>
 * 注意：卡券是现实中的卡券的抽象，其每一个实例对应现实中一个具体的卡券。
 */
@Aggregate
public class Coupon {
    private transient CouponId couponId;
    private transient CouponArchiveId archiveId;
    private String name;
    private transient TimeRange effectiveTime;
    private CouponStatus status = CouponStatus.Created;
    private transient Operator operator;
    private transient CouponOwner owner;
    // 来源信息
    private transient CouponSourceId sourceId;
    // 卡券参数
    private CouponParameter parameter;
    private LocalDateTime createTime;
    private LocalDateTime sendTime;
    private LocalDateTime receiveTime;
    private LocalDateTime consumeTime;
    private LocalDateTime cancelTime;
    private LocalDateTime backTime;

    protected transient CouponStateMachine sm;

    protected Coupon() {
        // StateMachine 通过该观察者变更卡券对象内部状态，卡券对象不对外公开状态变更方法
        Consumer<CouponStatus> watcher = (newStatus) -> this.status = newStatus;
        sm = new CouponStateMachine(this, watcher);
    }

    public Coupon(CouponId couponId, CouponArchiveId archiveId, String name, TimeRange effectiveTime, CouponParameter parameter) {
        this();
        this.couponId(couponId);
        this.archiveId(archiveId);
        this.setName(name);
        this.setEffectiveTime(effectiveTime);
        this.setCreateTime(LocalDateTime.now());
        this.setSourceId(CouponSourceId.EMPTY);
        this.setParameter(parameter);
    }

    public <T extends Coupon> T as(Class<T> clazz) {
        return (T) parameter.createCoupon(couponId, archiveId, name, effectiveTime);
    }

    public void giveTo(CouponSourceId sourceId, Operator operator, CouponOwner owner) {
        this.setSourceId(sourceId);
        this.operator(operator);
        this.owner(owner);
        this.activate();
        this.setSendTime(LocalDateTime.now());
    }

    public void receiveBy(CouponSourceId sourceId, CouponOwner by) {
        this.setSourceId(sourceId);
        this.owner(by);
        this.activate();
        this.setReceiveTime(LocalDateTime.now());
    }

    public void consumeBy(CouponOwner by) {
        this.checkOwner(by);
        sm.consume();
        this.setConsumeTime(LocalDateTime.now());
    }

    public void cancelBy(CouponSourceId sourceId, CouponOwner by) {
        this.checkOwner(by);
        this.setSourceId(sourceId);
        sm.cancel();
        this.setCancelTime(LocalDateTime.now());
    }

    public void backBy(CouponOwner by) {
        this.checkOwner(by);
        sm.back();
        this.setBackTime(LocalDateTime.now());
    }

    private void checkOwner(CouponOwner by) {
        if (by == null || !by.equals(owner)) {
            throw new BusinessException("卡券消费人和所属人不同");
        }
    }

    /**
     * 为定时扫描暴露
     */
    public void activate() {
        sm.activate();
    }

    /**
     * 为定时扫描暴露
     */
    public void expire() {
        sm.expire();
    }

    public CouponStatus currentStatus() {
        return status;
    }

    public CouponId couponId() {
        return couponId;
    }

    public void couponId(CouponId couponId) {
        this.couponId = couponId;
    }

    private String getId() {
        return couponId.getId();
    }

    private void setId(String couponId) {
        this.couponId = CouponId.of(couponId);
    }

    public CouponArchiveId archiveId() {
        return archiveId;
    }

    public void archiveId(CouponArchiveId archiveId) {
        this.archiveId = archiveId;
    }

    private String getArchiveId() {
        return archiveId.getId();
    }

    private void setArchiveId(String archiveId) {
        this.archiveId = CouponArchiveId.of(archiveId);
    }

    public String getName() {
        return name;
    }

    private void setName(String name) {
        this.name = name;
    }

    public TimeRange effectiveTime() {
        return effectiveTime;
    }

    private TimeRange getEffectiveTime() {
        return effectiveTime;
    }

    private void setEffectiveTime(TimeRange effectiveTime) {
        if (effectiveTime == null) {
            throw new IllegalArgumentException("请输入有效期");
        }
        this.effectiveTime = effectiveTime;
    }

    private Integer getStatus() {
        return status.ordinal();
    }

    private void setStatus(Integer index) {
        this.status = CouponStatus.values()[index];
    }

    public CouponOwner owner() {
        return owner;
    }

    private void owner(CouponOwner owner) {
        this.owner = owner;
    }

    private String getMemberId() {
        return owner.isMember() ? owner.getOwnerId() : null;
    }

    private void setMemberId(String memberId) {
        if (memberId != null && memberId.length() > 0) {
            this.owner = CouponOwner.of(MemberId.of(memberId));
        }
    }

    private String getFansId() {
        return owner.isMember() ? null : owner.getOwnerId();
    }

    private void setFansId(String fansId) {
        if (fansId != null && fansId.length() > 0) {
            this.owner = CouponOwner.of(FansId.of(fansId));
        }
    }

    private Character getOwnerType() {
        return owner.isMember() ? 'M' : 'F';
    }

    public boolean hasOperator() {
        return operator != null;
    }

    public Operator operator() {
        return operator;
    }

    private void operator(Operator operator) {
        this.operator = operator;
    }

    private String getOperatorId() {
        return operator == null ? null : operator.getOperatorId();
    }

    private void setOperatorId(String operatorId) {
        if (operatorId != null && operatorId.length() > 0) {
            this.operator = Operator.of(operatorId);
        }
    }

    public LocalDateTime getCreateTime() {
        return createTime;
    }

    private void setCreateTime(LocalDateTime createTime) {
        this.createTime = createTime;
    }

    public LocalDateTime getSendTime() {
        return sendTime;
    }

    private void setSendTime(LocalDateTime sendTime) {
        this.sendTime = sendTime;
    }

    public LocalDateTime getReceiveTime() {
        return receiveTime;
    }

    private void setReceiveTime(LocalDateTime receiveTime) {
        this.receiveTime = receiveTime;
    }

    public LocalDateTime getConsumeTime() {
        return consumeTime;
    }

    private void setConsumeTime(LocalDateTime consumeTime) {
        this.consumeTime = consumeTime;
    }

    public LocalDateTime getCancelTime() {
        return cancelTime;
    }

    private void setCancelTime(LocalDateTime cancelTime) {
        this.cancelTime = cancelTime;
    }

    public LocalDateTime getBackTime() {
        return backTime;
    }

    private void setBackTime(LocalDateTime backTime) {
        this.backTime = backTime;
    }

    public boolean hasSource() {
        return sourceId != null;
    }

    public CouponSourceId sourceId() {
        return sourceId;
    }

    private void setSourceId(CouponSourceId sourceId) {
        if (sourceId == null) {
            throw new IllegalArgumentException("请输入来源ID");
        }
        this.sourceId = sourceId;
    }

    public CouponParameter parameter() {
        return parameter;
    }

    private CouponParameter getParameter() {
        return parameter;
    }

    private void setParameter(CouponParameter parameter) {
        if (parameter == null) {
            throw new IllegalArgumentException("请输入卡券参数");
        }
        this.parameter = parameter;
    }
}
